<template>
  <div class="text-preview">
    <div class="header">
      <h2 class="title">文本预览</h2>
      <div class="actions">
        <el-button @click="selectFile" type="primary" plain>
          选择文件
        </el-button>
        <el-button v-if="fileContent" @click="copyToClipboard" type="success" plain>
          复制内容
        </el-button>
        <el-button v-if="fileContent" @click="downloadFile" type="info" plain>
          下载文件
        </el-button>
      </div>
    </div>

    <!-- 文件上传区域 -->
    <div v-if="!fileContent" class="upload-area" @click="selectFile" @dragover.prevent @drop.prevent="handleDrop">
      <i class="el-icon-document"></i>
      <p>点击选择或拖拽文本文件到此处</p>
      <p class="tip">支持 .txt、.md、.json、.csv、.log 等文本格式</p>
    </div>

    <!-- 文件信息 -->
    <div v-if="fileInfo" class="file-info">
      <div class="info-item">
        <span class="label">文件名:</span>
        <span class="value">{{ fileInfo.name }}</span>
      </div>
      <div class="info-item">
        <span class="label">文件大小:</span>
        <span class="value">{{ formatFileSize(fileInfo.size) }}</span>
      </div>
      <div class="info-item">
        <span class="label">文件类型:</span>
        <span class="value">{{ fileInfo.type || '文本文件' }}</span>
      </div>
      <div class="info-item">
        <span class="label">编码格式:</span>
        <el-select v-model="selectedEncoding" @change="handleEncodingChange" size="small">
          <el-option label="UTF-8" value="utf-8" />
          <el-option label="GBK" value="gbk" />
          <el-option label="GB2312" value="gb2312" />
          <el-option label="ASCII" value="ascii" />
        </el-select>
      </div>
    </div>

    <!-- 文本内容预览 -->
    <div v-if="fileContent" class="content-preview">
      <div class="content-header">
        <span class="content-title">文件内容</span>
        <div class="content-stats">
          <span>行数: {{ lineCount }}</span>
          <span>字符数: {{ charCount }}</span>
          <span>字节数: {{ byteCount }}</span>
        </div>
      </div>

      <div class="content-container">
        <div class="line-numbers" v-if="showLineNumbers">
          <div v-for="(line, index) in lines" :key="index" class="line-number">
            {{ index + 1 }}
          </div>
        </div>

        <div class="content-area">
          <pre class="content-text">{{ fileContent }}</pre>
        </div>
      </div>

      <div class="content-footer">
        <el-checkbox v-model="showLineNumbers">显示行号</el-checkbox>
        <el-checkbox v-model="wrapText">自动换行</el-checkbox>
      </div>
    </div>

    <!-- 隐藏的文件输入 -->
    <input ref="fileInput" type="file"
      accept=".txt,.md,.json,.csv,.log,.xml,.html,.css,.js,.ts,.vue,.jsx,.tsx,.py,.java,.c,.cpp,.h,.hpp"
      @change="handleFileSelect" style="display: none" />
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'

// 组件名称
defineOptions({
  name: 'TextPreview'
})

// 响应式数据
const fileContent = ref<string>('')
const fileInfo = ref<File | null>(null)
const selectedEncoding = ref<string>('utf-8')
const showLineNumbers = ref<boolean>(true)
const wrapText = ref<boolean>(false)

// DOM 引用
const fileInput = ref<HTMLInputElement>()

// 计算属性
const lines = computed(() => {
  return fileContent.value ? fileContent.value.split('\n') : []
})

const lineCount = computed(() => {
  return lines.value.length
})

const charCount = computed(() => {
  return fileContent.value.length
})

const byteCount = computed(() => {
  return new Blob([fileContent.value]).size
})

// 选择文件
const selectFile = () => {
  fileInput.value?.click()
}

// 处理文件选择
const handleFileSelect = (event: Event) => {
  const target = event.target as HTMLInputElement
  const file = target.files?.[0]
  if (file) {
    handleFile(file)
  }
}

// 处理拖拽文件
const handleDrop = (event: DragEvent) => {
  const files = event.dataTransfer?.files
  if (files && files.length > 0) {
    handleFile(files[0])
  }
}

// 处理文件
const handleFile = async (file: File) => {
  // 验证文件大小 (限制为 10MB)
  if (file.size > 10 * 1024 * 1024) {
    ElMessage.error('文件大小不能超过 10MB')
    return
  }

  // 验证文件类型
  const allowedTypes = ['text/plain', 'text/markdown', 'application/json', 'text/csv', 'text/xml', 'text/html', 'text/css', 'text/javascript', 'application/javascript']
  const textExtensions = ['.txt', '.md', '.json', '.csv', '.log', '.xml', '.html', '.css', '.js', '.ts', '.vue', '.jsx', '.tsx', '.py', '.java', '.c', '.cpp', '.h', '.hpp', '.rs', '.go']

  const isValidType = allowedTypes.includes(file.type) ||
    textExtensions.some(ext => file.name.toLowerCase().endsWith(ext))

  if (!isValidType && file.type && !file.type.startsWith('text/')) {
    ElMessage.warning('可能不是文本文件，但仍会尝试读取')
  }

  fileInfo.value = file

  try {
    await readFileContent(file)
    ElMessage.success('文件读取成功')
  } catch (error) {
    console.error('文件读取失败:', error)
    ElMessage.error('文件读取失败，请检查文件格式')
  }
}

// 读取文件内容
const readFileContent = (file: File): Promise<void> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()

    reader.onload = (e) => {
      try {
        const result = e.target?.result as string
        fileContent.value = result
        resolve()
      } catch (error) {
        reject(error)
      }
    }

    reader.onerror = () => {
      reject(new Error('文件读取失败'))
    }

    // 根据编码格式读取文件
    if (selectedEncoding.value === 'utf-8') {
      reader.readAsText(file, 'UTF-8')
    } else {
      reader.readAsText(file, selectedEncoding.value)
    }
  })
}

// 处理编码格式变化
const handleEncodingChange = async () => {
  if (fileInfo.value) {
    try {
      await readFileContent(fileInfo.value)
      ElMessage.success('编码格式已切换')
    } catch {
      ElMessage.error('编码格式切换失败')
    }
  }
}

// 复制到剪贴板
const copyToClipboard = async () => {
  try {
    await navigator.clipboard.writeText(fileContent.value)
    ElMessage.success('内容已复制到剪贴板')
  } catch {
    // 降级方案
    const textArea = document.createElement('textarea')
    textArea.value = fileContent.value
    document.body.appendChild(textArea)
    textArea.select()
    document.execCommand('copy')
    document.body.removeChild(textArea)
    ElMessage.success('内容已复制到剪贴板')
  }
}

// 下载文件
const downloadFile = () => {
  if (!fileInfo.value) return

  const blob = new Blob([fileContent.value], { type: 'text/plain;charset=utf-8' })
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')

  link.href = url
  link.download = fileInfo.value.name
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)

  URL.revokeObjectURL(url)
  ElMessage.success('文件下载已开始')
}

// 格式化文件大小
const formatFileSize = (bytes: number): string => {
  if (bytes === 0) return '0 Bytes'

  const k = 1024
  const sizes = ['Bytes', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))

  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
</script>

<style scoped>
.text-preview {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding-bottom: 15px;
  border-bottom: 1px solid #ebeef5;
}

.title {
  color: #303133;
  margin: 0;
  font-size: 1.5rem;
  font-weight: 600;
}

.actions {
  display: flex;
  gap: 10px;
}

.upload-area {
  border: 2px dashed #dcdfe6;
  border-radius: 8px;
  padding: 60px 20px;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.3s;
  background: #fafafa;
}

.upload-area:hover {
  border-color: #409eff;
  background: #f0f9ff;
}

.upload-area i {
  font-size: 48px;
  color: #c0c4cc;
  margin-bottom: 16px;
  display: block;
}

.upload-area p {
  margin: 8px 0;
  color: #606266;
}

.upload-area .tip {
  font-size: 12px;
  color: #909399;
}

.file-info {
  background: #f8f9fa;
  border-radius: 8px;
  padding: 20px;
  margin-bottom: 20px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 15px;
}

.info-item {
  display: flex;
  align-items: center;
  gap: 10px;
}

.info-item .label {
  font-weight: 500;
  color: #606266;
  min-width: 80px;
}

.info-item .value {
  color: #303133;
  word-break: break-all;
}

.content-preview {
  border: 1px solid #ebeef5;
  border-radius: 8px;
  overflow: hidden;
}

.content-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 20px;
  background: #f5f7fa;
  border-bottom: 1px solid #ebeef5;
}

.content-title {
  font-weight: 500;
  color: #303133;
}

.content-stats {
  display: flex;
  gap: 20px;
  font-size: 12px;
  color: #909399;
}

.content-container {
  display: flex;
  max-height: 500px;
  overflow: auto;
}

.line-numbers {
  background: #fafbfc;
  border-right: 1px solid #ebeef5;
  padding: 15px 10px;
  min-width: 60px;
  user-select: none;
}

.line-number {
  height: 20px;
  line-height: 20px;
  font-family: 'Courier New', monospace;
  font-size: 12px;
  color: #909399;
  text-align: right;
}

.content-area {
  flex: 1;
  overflow-x: auto;
}

.content-text {
  margin: 0;
  padding: 15px 20px;
  font-family: 'Courier New', 'Monaco', monospace;
  font-size: 13px;
  line-height: 20px;
  color: #303133;
  background: transparent;
  border: none;
  white-space: v-bind('wrapText ? "pre-wrap" : "pre"');
  word-break: v-bind('wrapText ? "break-word" : "normal"');
}

.content-footer {
  padding: 10px 20px;
  background: #f5f7fa;
  border-top: 1px solid #ebeef5;
  display: flex;
  gap: 20px;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .text-preview {
    padding: 15px;
  }

  .header {
    flex-direction: column;
    align-items: stretch;
    gap: 15px;
  }

  .actions {
    justify-content: center;
  }

  .file-info {
    grid-template-columns: 1fr;
  }

  .content-header {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }

  .content-stats {
    justify-content: center;
  }

  .line-numbers {
    min-width: 50px;
  }

  .content-text {
    font-size: 12px;
  }
}
</style>
